<?php

namespace app\backend\controller\sys;

use think\facade\Db;
use app\common\controller\Basic;
use app\common\service\sys\LogService;
use util\Dir;
use util\Database as dbOper;

/**
 * 数据库信息
 * 此控制器为特殊，无service\dao\model
 * @edit kaige
 * @update 2022-06-25
 * @来源 开源系列
 */
class Dbase extends Basic
{
    /**
     * 列表--读取数据库列表
     */
    public function datalist()
    {
        if ($this->request->isGet()) {
            $data = Db::query("SHOW TABLE STATUS");
            return json(array(
                "code" => 0,
                "msg" => "ok",
                "count" => count($data),
                "data" => $data
            ));
        }
        return _jerr(1000);
    }

    /**
     * 数据表优化
     */
    public function optimize()
    {
        if ($this->request->isPost()) {
            $id = input('param.id/a');
            if (empty($id)) {
                return json(array("error" => 1, "msg" => "请选择您要优化的数据表！"));
            }
            if (!is_array($id)) {
                $table[] = $id;
            } else {
                $table = $id;
            }
            $tables = implode('`,`', $table);
            $res = Db::query("OPTIMIZE TABLE `{$tables}`");
            if ($res) {
                (new LogService())->add([
                    'uid' => $this->user['id'],
                    'un' => $this->user['un'],
                    'node' => $this->node,
                    'create_time' => time(),
                    'ip' => _gip(),
                    'action' => '数据表优化',
                    'content'  => json_encode($tables),
                ]);
                return json(array("error" => 0, "msg" => "数据表优化完成。"));
            }
            return json(array("error" => 1, "msg" => "数据表优化失败。"));
        }
        return _jerr(1000);
    }

    /**
     * 数据表修复
     */
    public function repair()
    {
        if ($this->request->isPost()) {
            $id = input('param.id/a');
            if (empty($id)) {
                return json(array("error" => 1, "msg" => "请选择您要修复的数据表！"));
            }
            if (!is_array($id)) {
                $table[] = $id;
            } else {
                $table = $id;
            }
            $tables = implode('`,`', $table);
            $res = Db::query("REPAIR TABLE `{$tables}`");
            if ($res) {
                (new LogService())->add([
                    'uid' => $this->user['id'],
                    'un' => $this->user['un'],
                    'node' => $this->node,
                    'create_time' => time(),
                    'ip' => _gip(),
                    'action' => '数据表修复',
                    'content'  => json_encode($tables),
                ]);
                return json(array("error" => 0, "msg" => "数据表修复完成。"));
            }
            return json(array("error" => 1, "msg" => "数据表修复失败！"));
        }
        return _jerr(1000);
    }

    /**
     * 备份数据表
     */
    public function export()
    {
        if ($this->request->isPost()) {
            $id = input('param.id/a');
            $start = 0;
            if (empty($id)) {
                return json(array("error" => 1, "msg" => "请选择您要备份的数据表！"));
            }

            if (!is_array($id)) {
                $tables[] = $id;
            } else {
                $tables = $id;
            }

            //读取备份配置
            $config = array(
                'path'     => '../backup/database/',
                'part'     => 20971520,
                'compress' => 1,
                'level'    => 4,
            );

            //检查是否有正在执行的任务
            $lock = $config['path'] . "backup.lock";
            if (is_file($lock)) {
                return json(array("error" => 1, "msg" => "检测到有一个备份任务正在执行，请稍后再试！"));
            } else {
                if (!is_dir($config['path'])) {
                    (new Dir())->create($config['path'], 0755, true);
                }
                //创建锁文件
                file_put_contents($lock, $this->request->time());
            }

            //生成备份文件信息
            $file = [
                'name' => date('Ymd-His', $this->request->time()),
                'part' => 1,
            ];

            // 创建备份文件
            $database = new dbOper($file, $config);
            if ($database->create() !== false) {
                // 备份指定表
                foreach ($tables as $table) {
                    $start = $database->backup($table, $start);
                    while (0 !== $start) {
                        if (false === $start) {
                            return json(array("error" => 1, "msg" => "备份出错！"));
                        }
                        $start = $database->backup($table, $start[0]);
                    }
                }
                // 备份完成，删除锁定文件
                unlink($lock);
                (new LogService())->add([
                    'uid' => $this->user['id'],
                    'un' => $this->user['un'],
                    'node' => $this->node,
                    'create_time' => time(),
                    'ip' => _gip(),
                    'action' => '数据表备份',
                    'content'  => json_encode($id),
                ]);
                return json(array("error" => 0, "msg" => "备份完成。"));
            }
            return json(array("error" => 1, "msg" => "备份出错！"));
        }
        return _jerr(1000);
    }

    /**
     * 备份文件列表
     */
    public function importlist()
    {
        if ($this->request->isGet()) {
            $path = '../backup/database/';
            if (!is_dir($path)) {
                (new Dir())->create($path);
            }
            $flag = \FilesystemIterator::KEY_AS_FILENAME;
            $glob = new \FilesystemIterator($path,  $flag);

            $data_list = [];
            $dnum = 0;
            foreach ($glob as $name => $file) {
                if (preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql(?:\.gz)?$/', $name)) {
                    $name = sscanf($name, '%4s%2s%2s-%2s%2s%2s-%d');
                    $date = "{$name[0]}-{$name[1]}-{$name[2]}";
                    $time = "{$name[3]}:{$name[4]}:{$name[5]}";
                    $part = $name[6];

                    if (isset($data_list["{$date} {$time}"])) {
                        $info = $data_list["{$date} {$time}"];
                        $info['part'] = max($info['part'], $part);
                        $info['size'] = $info['size'] + $file->getSize();
                    } else {
                        $info['part'] = $part;
                        $info['size'] = $file->getSize();
                    }

                    $extension        = strtoupper($file->getExtension());
                    $info['compress'] = ($extension === 'SQL') ? '无' : $extension;
                    $info['time']     = strtotime("{$date} {$time}");

                    $data_list[$dnum] = $info;
                    $dnum++;
                }
            }

            return json(array(
                "code" => 0,
                "msg" => "ok",
                "count" => count($data_list),
                "data" => $data_list
            ));
        }
        return _jerr(1000);
    }

    /**
     * 恢复数据库
     */
    public function import()
    {
        if ($this->request->isGet()) {
            $path_bak = '../backup/database/';
            $id = input('param.id/a');
            if (empty($id)) {
                return json(array("error" => 1, "msg" => "请选择您要恢复的备份文件！"));
            }

            $name  = date('Ymd-His', $id[0]) . '-*.sql*';
            $path  = $path_bak . $name;
            $files = glob($path);
            $list  = array();
            foreach ($files as $name) {
                $basename = basename($name);
                $match    = sscanf($basename, '%4s%2s%2s-%2s%2s%2s-%d');
                $gz       = preg_match('/^\d{8,8}-\d{6,6}-\d+\.sql.gz$/', $basename);
                $list[$match[6]] = array($match[6], $name, $gz);
            }
            ksort($list);

            // 检测文件正确性
            $last = end($list);
            if (count($list) === $last[0]) {
                foreach ($list as $item) {
                    $config = [
                        'path'     => $path_bak,
                        'compress' => $item[2]
                    ];
                    $database = new dbOper($item, $config);
                    $start = $database->import(0);
                    // 导入所有数据
                    while (0 !== $start) {
                        if (false === $start) {
                            return json(array("error" => 1, "msg" => "数据恢复出错！"));
                        }
                        $start = $database->import($start[0]);
                    }
                }
                (new LogService())->add([
                    'uid' => $this->user['id'],
                    'un' => $this->user['un'],
                    'node' => $this->node,
                    'create_time' => time(),
                    'ip' => _gip(),
                    'action' => '数据表恢复',
                    'content'  => json_encode($id),
                ]);
                return json(array("error" => 0, "msg" => "数据恢复完成。"));
            }
            return json(array("error" => 1, "msg" => "备份文件可能已经损坏，请检查！"));
        }
        return _jerr(1000);
    }

    /**
     * 备份文件删除
     **/    
    public function importdel()
    {
        if ($this->request->isGet()) {
            $path_bak = '../backup/database/';
            $id = input('param.id/a');
            if (empty($id)) {
                return json(array("error" => 1, "msg" => "请选择您要删除的备份文件！"));
            }
            $name  = date('Ymd-His', $id[0]) . '-*.sql*';
            $path = $path_bak . $name;
            array_map("unlink", glob($path));
            if (count(glob($path)) && glob($path)) {
                return json(array("error" => 1, "msg" => "备份文件删除失败，请检查权限！"));
            }
            (new LogService())->add([
                'uid' => $this->user['id'],
                'un' => $this->user['un'],
                'node' => $this->node,
                'create_time' => time(),
                'ip' => _gip(),
                'action' => '备份文件删除',
                'content'  => json_encode($id),
            ]);
            return json(array("error" => 0, "msg" => "备份文件删除成功。"));
        }
        return _jerr(1000);
    }
}
